
#pragma once

#include "type.h"
#include "log.h"

#include <vector>
#include <map>
#include <list>
#include <string>

using namespace std;

static const int SQUAD_COUNT = 5;
static const int LINES_PER_SQUAD = 3;
static const int TOTAL_LINES = SQUAD_COUNT * LINES_PER_SQUAD;
static const int ROWS_PER_LINE = 6;
static const int SQUAD_CASE_COUNT = LINES_PER_SQUAD * ROWS_PER_LINE;
static const int GRIDS_COUNT = TOTAL_LINES * ROWS_PER_LINE;

static const int GRID_WIDTH = 22;
static const int GRID_HEIGHT = 42;
static const int FORMATION_MARGIN = 20;
static const int FORMATION_SPACE = 50;
static const int SQUAD_SPACE = 20;

static const float MELEE_SPACE = 1.25f * GRID_WIDTH;
static const float MELEE_H_RANGE = 3.0f * GRID_WIDTH;
static const float MELEE_V_RANGE = 2.0f * GRID_HEIGHT;

static const char* SQUAD_ORDER_NAMES[] = {"HOLD", "SLOW", "FAST"};

enum class SquadOrder {
	hold,
	slow,
	fast,
};

enum class ArmyFaction {
	left,
	right,
};

struct SquadInfo {
	int number;
	SquadOrder order;
	string units[SQUAD_CASE_COUNT];
};

struct GridInfo {
	int globalGridIndex;
	int globalLineIndex;
	int squadIndex;
	int lineIndex;
	int rowIndex;

	GridInfo() {
		memset(this, 0, sizeof(GridInfo));
	}

	GridInfo(int gridIndex) {
		set(gridIndex);
	}

	void set(int gridIndex) {
		if (gridIndex == -1) {
			memset(this, -1, sizeof(GridInfo));
		} else {
			globalGridIndex = gridIndex;
			globalLineIndex = gridIndex / ROWS_PER_LINE;
			squadIndex = globalLineIndex / LINES_PER_SQUAD;
			lineIndex =globalLineIndex % LINES_PER_SQUAD;
			rowIndex = gridIndex % ROWS_PER_LINE;
		}
	}

	void set(int squad, int line, int row) {
		squadIndex = squad;
		lineIndex = line;
		rowIndex = row;

		globalLineIndex = squad * LINES_PER_SQUAD + line;
		globalGridIndex = globalLineIndex * ROWS_PER_LINE + row;

	}
};

struct UnitInfo {
	string name;
	string creature;
	string weapon;
	string armor;
	string shield;
	string special;

	int id;

	string getValue(EquipmentType type) {
		switch (type) {
			case EquipmentType::creature:
				return creature;
			case EquipmentType::weapon:
				return weapon;
			case EquipmentType::armor:
				return armor;
			case EquipmentType::shield:
				return shield;
			case EquipmentType::special:
				return special;
			default:
				return "";
		}
	}
};

struct FormationInfo {
	string name;
	vector<UnitInfo> units;
	SquadOrder orders[SQUAD_COUNT];
	string grids[TOTAL_LINES][ROWS_PER_LINE];

	UnitInfo getUnitInfo(const string& name) const {
		for (size_t i = 0; i < units.size(); i++) {
			if (units[i].name == name) {
				return units[i];
			}
		}

		_DHLOG("warning: not found unit info [%s]", name.c_str());
		return UnitInfo();
	}

	int getUnitItemMaxID() {
		int max_id = 0;
		for (auto item : units) {
			if (item.id > max_id) {
				max_id = item.id;
			}
		}

		return max_id + 1;
	}

	int getUnitID(const string& name) const {
		int ret = 0;

		for(auto it = units.begin(); it != units.end(); it++) {
			if(it->name == name) {
				ret = it->id;
				break;
			}
		}

		return ret;
	}

	string getUnitName(int id) const {
		string ret;

		for(auto it = units.begin(); it != units.end(); it++) {
			if(it->id == id) {
				ret = it->name;
				break;
			}
		}

		return ret;
	}

	void clear() {
		memset(orders, 0, sizeof(orders));

		for(int i = 0; i < TOTAL_LINES; i++) {
			for(int j = 0; j < ROWS_PER_LINE; j++) {
				grids[i][j] = "";
			}
		}
	}

	size_t getChunkByteCount() const {
		return 149 + 62 * units.size();
	}

	const char* toBytes() const;
	static FormationInfo fromBytes(char const *buf);
};
